#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：prod_ctrl_service 
@File    ：fill_electrolyte.py
@IDE     ：PyCharm 
@Author  ：孔令伟
@Date    ：8/28/24 8:47 AM 
"""
import json
from copy import deepcopy
from decimal import Decimal
from typing import Dict, Union

from mg_app_framework import MesCode, get_organization, get_logger

from common.basic_mongo import get_mongo_collection
from common.basic_redis import get_redis_conn
from common.constant import RetrospectType
from common.retrospect_operate.handle import filter_device_code_workorder_config, publish_retrospect_nodes
from prod_ctrl_service.tool.tool import check_basic_param, get_time, batch_make_retros_in, \
    make_retrospect_data_and_publish, get_ng_name, send_unqualified_http, insert_unqualified_records_backup

"""
其它-补液-补液数据上传校验(IF025)
人工补液完成后，上位机上上传补液数据，包括扫码进站时的前称重数量和补液后的称重数量。
"""


async def fill_electrolyte_check(
        params: Dict[str, Union[str, Decimal, int]]
) -> Dict[str, Union[str, Dict]]:
    """
    人工补液完成后，上位机上上传补液数据，包括扫码进站时的前称重数量和补液后的称重数量。
    :param params:{
                   "call_id":"JQ00386",  #接口调用方认证ID, string
                   "trace_id":"8941d16b-0581-4e68-8940-a651afff4282",  #接口调用ID, string
                   "upperpc_code": "UP011",    #上位机编码, string
                   "user_code":"Y001",  #员工编码, string
                   "equip_code":"ZP001", #设备编码
                   "process_code":"2050",   # 工序编码, string, 必输
                   "cell_sn": "WW202408140001", #电芯码, string
                   "op_time": "2024-08-12 13:19:25",   #补液时间, datetime, 非必输
                   "before_weight": "0.95",   #注液进站时前重量, decimal,
                   "after_weight": "1",   #补液后重量, decimal
                   "is_ng": 1,   #补液后是否NG, int, 0：OK，1：NG
                   "ng_code": ""   #NG代码, 数组, 非必输
                }
    :return:{
                   "code": "success",   #接口应答状态代码, success：成功; fail：失败, string
                   "data": {}",  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
                   "info": "OK"   #接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
                }
    """
    error_msg = ""
    # '接口调用方认证ID/接口调用ID/上位机编码/员工编码/设备编码'是每一个接口都有且必填的入参，检查这些参数是否填写
    basic_param_check_result, basic_param_check_message = check_basic_param(params=params)
    if not basic_param_check_result:
        error_msg += basic_param_check_message

    before_weight, after_weight = params.get("before_weight", None), params.get(
        "after_weight", None
    )
    cell_sn, is_ng = params.get("cell_sn", None), params.get("is_ng", None)
    if not any([before_weight, after_weight, cell_sn]) or is_ng not in [0, 1]:
        error_msg += "请检查是否已填写'电芯码/补液数据/是否NG'"
    equip_code = params.get("equip_code")
    process_code = params.get("process_code")
    workorder_config = await filter_device_code_workorder_config(device_code=equip_code)
    bom_info = workorder_config.bom_info
    if not bom_info.product_out:
        error_msg += f"设备{equip_code}的BOM信息未配置完整;"
    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
        }
        return result
    prod_result_param_list = []  # 根据入参构建的需要写入到出站参数表的数据
    unqualified_records: list = []  # 根据入参构建的需要写入到不合格品记录表的数据
    unqualified_records_backup: list = []  # 根据入参构建的需要写入到不合格品记录表的数据的备份
    # 补液工序需要为传入的电芯码建立补液的进出站追溯数据
    # 进站追溯数据
    work_order = workorder_config.workorder_code
    insert_data_list = batch_make_retros_in(wip_list=[params['cell_sn']], work_order=work_order)
    work_order_code = insert_data_list[0]["work_order_code"]
    try:
        for rec_item in insert_data_list:
            await publish_retrospect_nodes(device_code=equip_code, retrospect_items=[rec_item])
        get_logger().info(f"码值{[params['cell_sn']]}进站追溯数据已建立")
    except ValueError as e:
        error_msg += str(e)
    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
        }
        return result
    # 出站追溯数据
    is_ng = params['is_ng']
    ng_list = [{"code": ng_code, "num": 1} for ng_code in params.get("ng_code", [])]
    if after_weight > before_weight:
        # 当补液后数据>补液前数据，说明在补液过程中出现了原材料的消耗，因此除了电芯码在补液工序的进出站追溯数据，
        # 还需要建立原材料的进站追溯数据并进行原材料的扣减操作；
        param = {
            "procedure_code": process_code,
            "device_code": equip_code,
            "is_ng": is_ng,
            "qr_code": cell_sn,
            "ng_list": ng_list,
            # 使用bom产出物料第一个作为产出追溯的物料编码，否则通过查询进站追溯数据获取物料信息(即默认逻辑)
            "material_code": bom_info.product_out[0].material_code,
            # 使用这两个字段(interface_type/material_quota)使得方法中在扣减原材料时使用（补液后重量-补液前重量）的数值，而不是使用BOM配置的物料扣减数量
            "interface_type": "fill_electrolyte",
            "material_quota": after_weight - before_weight
        }
        retros_result, retros_msg, input_lot_no_list = await make_retrospect_data_and_publish(param=param)
        if not retros_result:
            error_msg += retros_msg
    else:
        # 当补液后数据<补液前数据，说明在补液过程没有消耗原材料，只需要建立电芯码在补液工序的进出站追溯数据。
        retros_ng_list = [{"qr_code": cell_sn,
                           "op_type": RetrospectType.PRODUCT_OUT,
                           "check_result": "" if is_ng == 0 else "不合格",
                           "work_order_code": work_order_code,
                           "ng_list": ng_list,
                           "material_code": bom_info.product_out[0].material_code
                           }]
        try:
            await publish_retrospect_nodes(device_code=equip_code, retrospect_items=retros_ng_list)
            get_logger().info(f"码值{[cell_sn]}进站追溯数据已建立")
        except ValueError as e:
            error_msg += str(e)

    # 构建出站参数表数据
    prod_result_param_list.append({
        "call_id": params.get("call_id"),  # 接口调用方认证ID
        "trace_id": params.get("trace_id"),  # 接口调用ID
        "upperpc_code": params.get("upperpc_code"),  # 上位机编码
        "user_code": params.get("user_code"),  # 员工编码
        "equip_code": params.get("equip_code"),  # 设备编码
        "process_code": params.get("process_code"),  # 工序编码
        "wip_no": cell_sn,
        "param_list": params.get("param_list", {}),
        "time": get_time(),
        "data_source": "025"  # 此字段用于标记由哪个接口构建的数据
    })
    if is_ng == 1:
        # 同一个码值的不合格品记录数目取决于有多少个ng-code
        for ng_item in ng_list:
            unqualified_reason_name = await get_ng_name(ng_item['code'])
            unqualified_data = {
                "qr_code": cell_sn,  # 码值
                "product_code": bom_info.product_out[0].material_code,  # 不合格品物料编码
                "product_name": bom_info.product_out[0].material_name,  # 不合格品物料名称
                "process_code": workorder_config.procedure_code,  # 生产工序编码
                "process_name": workorder_config.procedure_name,  # 生产工序名称
                "device_code": workorder_config.device_code,  # 对应生产设备编码
                "device_name": workorder_config.device_name,  # 对应生产设备编码
                "production_date": get_time()[:10],  # 出库日期，格式为 %Y-%m-%d
                "unqualified_reason_code": ng_item['code'],  # 不合格原因编码（对应不合格品基础配置中的不良品编码）
                "unqualified_reason_name": unqualified_reason_name,  # 不合格原因（对应不合格品基础配置中的不良品名称）
                "ok_qty": None,  # 合格数
                "ng_qty": None,  # 不合格数
                'user_name': params.get('user_code', '')
            }
            unqualified_records.append(unqualified_data)
            unqualified_data_backup = deepcopy(unqualified_data)
            unqualified_data_backup.update({"source": '025'})
            unqualified_records_backup.append(unqualified_data_backup)

    # todo 写入出站参数
    # if prod_result_param_list:
    #     await insert_prod_result_param(params=prod_result_param_list)
    # 将不合格品记录发送至不合格品app
    if unqualified_records:
        await send_unqualified_http(unqualified_records)
    # 本地将不合格品记录备份写入备份表
    if unqualified_records_backup:
        await insert_unqualified_records_backup(unqualified_records_backup)
    # 记录补液数据
    data = {
        "call_id": params.get("call_id"),  # 接口调用方认证ID, string
        "trace_id": params.get("trace_id"),  # 接口调用ID, string
        "upperpc_code": params.get("upperpc_code"),  # 上位机编码, string
        "user_code": params.get("user_code"),  # 员工编码, string
        "equip_code": params.get("equip_code"),  # 设备编码
        "process_code": params.get("process_code"),  # 工序编码
        "cell_sn": params.get("cell_sn"),  # 电芯码, string
        "op_time": params.get("op_time", ""),  # 补液时间, datetime, 非必输
        "before_weight": params.get("before_weight"),  # 注液进站时前重量, decimal,
        "after_weight": params.get("after_weight"),  # 补液后重量, decimal
        "is_ng": params.get("is_ng"),  # 补液后是否NG, int, 0：OK，1：NG
        "ng_code": params.get("ng_code", ""),  # NG代码, 数组, 非必输
        "time": get_time()
    }
    await insert_data(data)

    result = {
        "code": MesCode.success,  # 接口应答状态代码, success：成功; fail：失败, string
        "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
        "info": "OK",  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
    }
    return result


async def insert_data(data):
    """

    :param data:
    :return:
    """
    collection = get_mongo_collection(
        database_name=f"{get_organization()}_mes",
        collection_name=f"fill_electrolyte_record",
    )
    await collection.insert_one(data)


async def material_calculate(device_code: str, after_weight: float, before_weight: float):
    """
    原料扣减操作
    :param after_weight:
    :param before_weight:
    :param device_code:
    :return:
    """
    # todo  原料扣减操作
    rd_conn = get_redis_conn()
    ts_key = f"xxx"  # todo,redis会维护一个批次码与其对应的物料数量的映射关系，通过这个key来获取，待开发
    qr_code_material_code_num_map: dict = await rd_conn.get(ts_key)
    bom_info = await rd_conn.get(f"workorder_mgm~{device_code}")
    bom_info = json.loads(bom_info)
    for rec in bom_info["bom_info"].get("product_in", []):
        if rec.get("code_type") == "batch_code":
            material_code = rec.get("material_code")
            qr_code_material_code_num_map[material_code] -= after_weight - before_weight
    await rd_conn.set(ts_key, qr_code_material_code_num_map)
